#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/1/11 12:08
# @Author : 闫峰
# @File    : execute_strategy.py
import datetime
import decimal
import json
from decimal import Decimal

import requests

from common.setting import ensure_path_sep
from utils.mongodb_control.batch_by_mongodb import MongoDBByBatch
from utils.mysqlUtils.db_pool import Database
import os
import threading
import time
from common.setting import ensure_path_sep
from utils.logging_tool.log_control import LogHandler
from utils.mysqlUtils.db_pool import Database
from utils.mysqlUtils.mongodb_utils import MongoDBLink
from utils.logging_tool.init_logger import init_logger
from utils.otherutils import count_time

INFO, ERROR, WARNING = init_logger()


class ExecuteStrategy:
    zhcxkj_center_strategy_comprehensive_db = Database(ensure_path_sep("\\config\\shopeev2_config.yaml.bak"),
                                                       "HWY-ZH-GZ-RDS-strategy")
    zhcxkj_center_listing_comprehensive_db = Database(ensure_path_sep("\\config\\shopeev2_config.yaml.bak"),
                                                      "zhcxkj_center_listing_comprehensive")
    t_sku_strategy_relation_modb = MongoDBLink(
        uri='mongodb://reader:Vly07YmbY0kLt80Y?@121.37.240.172:8635,121.37.230.118:8635/test?authSource=admin&replicaSet=replica',
        dbname='zhcxkj_center_strategy', coll_name="t_sku_strategy_relation")

    def __init__(self, strategy_id, source_id):
        get_t_allegro_marketing_strategy = """
         SELECT status,sku_template_id,listing_template_id,strategy_type,price_template_id
         FROM zhcxkj_center_strategy_comprehensive.t_allegro_marketing_strategy a
        WHERE a.id = %s
        """
        get_t_allegro_price_filter_template = """
         SELECT 
         price_adjust_plan,
         calculator_formula_id,
         price_percentage,
         price_adjust_type,
         min_price,
         max_price,
         min_profit,
         max_profit,
         status,
         sell_price_type
         FROM zhcxkj_center_strategy_comprehensive.t_allegro_price_filter_template a
         WHERE a.id = %s
                """
        get_specify_list = f"""
        select sku,source_sku,offer_id 
        from zhcxkj_center_strategy_comprehensive.t_allegro_strategy_sku
        where strategy_id =%s
        """
        specify_list = self.zhcxkj_center_strategy_comprehensive_db.fetch_all_tuple(get_specify_list, (strategy_id,))
        self.sku_list = []
        self.source_sku_list = []
        self.offer_id_list = []
        if len(specify_list):
            self.sku_list = [i[0] for i in specify_list if i[0] is not None]
            self.source_sku_list = [i[1] for i in specify_list if i[1] is not None]
            self.offer_id_list = [i[2] for i in specify_list if i[2] is not None]

        t_allegro_marketing_strategy = self.zhcxkj_center_strategy_comprehensive_db.fetch_all_tuple(
            get_t_allegro_marketing_strategy, (strategy_id,))
        status = t_allegro_marketing_strategy[0][0]
        if status == 0:
            raise Exception("策略状态是禁用")
        self.strategy_id = strategy_id
        self.source_id = source_id
        self.sku_template_id = t_allegro_marketing_strategy[0][1]
        self.listing_template_id = t_allegro_marketing_strategy[0][2]
        self.strategy_type = t_allegro_marketing_strategy[0][3]
        self.price_template_id = t_allegro_marketing_strategy[0][4]
        self.num = source_id % 8 + 1
        if self.strategy_type == 5:
            t_allegro_price_filter_template = self.zhcxkj_center_strategy_comprehensive_db.fetch_all_tuple(
                get_t_allegro_price_filter_template, (self.price_template_id,))
            self.price_adjust_plan = t_allegro_price_filter_template[0][0]
            self.calculator_formula_id = t_allegro_price_filter_template[0][1]
            self.price_percentage = t_allegro_price_filter_template[0][2]
            self.price_adjust_type = t_allegro_price_filter_template[0][3]
            self.min_price = t_allegro_price_filter_template[0][4]
            self.max_price = t_allegro_price_filter_template[0][5]
            self.min_profit = t_allegro_price_filter_template[0][6]
            self.max_profit = t_allegro_price_filter_template[0][7]
            self.allegro_price_filter_template_status = t_allegro_price_filter_template[0][8]
            self.sell_price_type = t_allegro_price_filter_template[0][9]
            if not self.allegro_price_filter_template_status:
                raise Exception("调价模板状态未开启")

    def execute_strategy(self):
        # 商品过滤规则集合
        t_allegro_listing_template_detail = f"t_allegro_listing_template_detail_{self.listing_template_id}"
        t_allegro_listing_template_detail_modb = MongoDBLink(
            uri='mongodb://reader:Vly07YmbY0kLt80Y?@121.37.240.172:8635,121.37.230.118:8635/test?authSource=admin&replicaSet=replica',
            dbname='zhcxkj_comprehensive_allegro', coll_name=t_allegro_listing_template_detail)
        t_allegro_listing_template_detail_modb_MongoDBByBatchExe = MongoDBByBatch(
            t_allegro_listing_template_detail_modb)
        offer_id_set = set()
        if any([self.source_sku_list, self.sku_list, self.offer_id_list]):
            offer_id_set = self.get_t_allegro_listing_list_by_specify(t_allegro_listing_template_detail_modb)
        else:
            offer_id_set = set()
            filters_ObjectId = {
                "$and": [
                    {"sourceId": self.source_id}
                ]
            }
            projection1 = {"_id": 1, "offerId": 1, "sku": 1, "sourceSku": 1}
            generator = t_allegro_listing_template_detail_modb_MongoDBByBatchExe.get_data(filters_ObjectId, projection1,
                                                                                      1000)
            count = 0
            while True:
                try:
                    t_allegro_listing_list_batch = next(generator)
                    # 模版结果表中存在sku没有的数据 14979366344
                    t_allegro_listing_list_batch = list(
                        filter(lambda x: x.get('sku', None) != None, t_allegro_listing_list_batch))

                    # 如果策略选择了SKU分级策略，取交集
                    if self.sku_template_id:
                        t_allegro_listing_list_batch = self.get_t_allegro_listing_list_by_t_sku_strategy_relation(
                            t_allegro_listing_list_batch)
                    # 如果是调价模板
                    if self.strategy_type == 5:
                        offer_id_set |= self.get_offer_id_set_by_adjust_price(t_allegro_listing_list_batch)
                    else:
                        offer_id_set |= set(map(lambda x: x.get('offerId'), t_allegro_listing_list_batch))
                except StopIteration:
                    break
                except Exception as e:
                    raise e
                print(f"遍历分级模板{count}个 得到自动调价的数据{len(offer_id_set)}")
        print(f"source_id={self.source_id}预期{offer_id_set}")
        # 得到一个 offer_id_set
        self.check_actual_offer_id_set(offer_id_set)

    def check_actual_offer_id_set(self, expect_offer_id_set):

        t_allegro_strategy_adjust_price_detail_db = MongoDBLink(
            uri='mongodb://reader:Vly07YmbY0kLt80Y?@121.37.240.172:8635,121.37.230.118:8635/test?authSource=admin&replicaSet=replica',
            dbname='zhcxkj_comprehensive_allegro', coll_name="t_allegro_strategy_adjust_price_detail")
        t_allegro_listing_off_strategy_detail_db = MongoDBLink(
            uri='mongodb://reader:Vly07YmbY0kLt80Y?@121.37.240.172:8635,121.37.230.118:8635/test?authSource=admin&replicaSet=replica',
            dbname='zhcxkj_comprehensive_allegro', coll_name="t_allegro_listing_off_strategy_detail")
        if self.strategy_type == 4:
            filter_sql = {"sourceId": self.source_id, "strategyId": self.strategy_id}
            field_ = {"offerId": 1}
            res = t_allegro_listing_off_strategy_detail_db.select(filter_sql, field_)
            actual_offer_id_set = set(map(lambda x: x['offerId'], res))
        elif self.strategy_type == 5:
            filter_sql = {"sourceId": self.source_id, "strategyId": self.strategy_id}
            field_ = {"offerId": 1}
            res = t_allegro_strategy_adjust_price_detail_db.select(filter_sql, field_)
            actual_offer_id_set = set(map(lambda x: x['offerId'], res))
        if expect_offer_id_set != actual_offer_id_set:
            print(f"{self.strategy_id}_{self.source_id}验证存在问题")
        else:
            print(f"{self.strategy_id}_{self.source_id}验证无问题")

    @count_time.timer_decorator
    def getCalculationGrossProfitMarginViewModelList(self, calculationFormulaId, orderSourceId, listing_id_list):
        request_json = {
            "input": [

            ]
        }
        for lising in listing_id_list:
            sku_model = {
                "businessTypeCode": "AllegroLocalWarehouse",
                "calculationFormulaId": calculationFormulaId,
                "country": "PL",
                "customerId": 1,
                "orderSourceId": orderSourceId,
                "platformId": 67,
                "price": str(lising[2]),
                "sku": lising[3],
                "warehouseId": 5312,
                "warehouseTypeId": 1
            }
            request_json['input'].append(sku_model)
        request_headers = {
            "Authorization": "Basic YXBpdXNlcjpFN0kxLWxJX2QjOGRscw=="
        }
        res = requests.post(
            url="http://calculatorapi.zhcxkj.com/api/calculationFormula/calculationPrice/getCalculationGrossProfitMarginViewModelList",
            headers=request_headers,
            json=request_json)
        sku_GrossProfitMargin_map = {}
        for data in res.json()['data']:
            sku_GrossProfitMargin_map[data["sku"] + "@" + '{:.2f}'.format(data["price"])] = str(
                data['grossProfitMargin'])
        return sku_GrossProfitMargin_map

    @count_time.timer_decorator
    def get_price(self, calculationFormulaId, orderSourceId, sku):
        request_json = {
            "input": {
                "businessTypeCode": "AllegroLocalWarehouse",
                "country": "PL",
                "PlatformId": 67,
                "customerId": 1,
                "orderSourceId": orderSourceId,
                "platformId": 1,
                "skuList": [
                    sku
                ],
                "warehouseId": 5312,
                "warehouseTypeId": 1,
                "CalculationFormulaId": calculationFormulaId
            }
        }
        request_headers = {
            "Authorization": "Basic aXJvYm90Ym94OmkxcjVvOEJvdGJveA=="
        }
        res = requests.post(
            url="http://priceapi.zhcxkj.com/api/CalculationFormula/CalculationPrice/GetCalculatorPriceViewModelList",
            headers=request_headers,
            json=request_json)
        if res.status_code == 200:
            return Decimal(str(res.json()['data'][0]['Price']))

    def filter_adjust_model(self, current_offer_id_set):
        # 还原

        get_data_set = f"""
                SELECT offer_id,price,sku
        FROM `zhcxkj_center_listing_comprehensive`.`t_allegro_listing_{self.num}` 
        WHERE offer_id in %s
        and source_id = %s
        """
        listing_id_list = self.zhcxkj_center_listing_comprehensive_db.fetch_all_tuple(get_data_set,
                                                                                      (current_offer_id_set,self.source_id))
        filter_listing_id_list = None
        # 最终售价
        if self.price_adjust_plan == 1:
            filter_listing_id_list = self.handlerFinalsellingprice(listing_id_list)
        # 计算器公式
        elif self.price_adjust_plan == 2:
            filter_listing_id_list = self.handlerCalculatorFormulas(listing_id_list)

        return set(map(lambda x: x[0], filter_listing_id_list))

    def handlerFinalsellingprice(self, listing_id_list):
        """
        调价方案： 最终售价
        根据最终售价算法，得到调整后的价格
        1 如果类型是价格区间
            验证 调整后的价格 在  原始价格*self.min_price ——  原始价格*self.max_price之间
        2 如果是利润区间
            调研计算器得到每一条数据的利润率  验证利润率 是否在 self.min_profit—— self.max_profit 之间
        :param listing_id_list:
        :return:
        """
        filter_listing_id_list = None
        listing_id_list_map = None
        # 将 listing_id_list -> offer_id,price,adjust_price,sku
        if self.sell_price_type == 1:
            listing_id_list_map = list(
                map(lambda x: (x[0], x[1], x[1] * (100 - self.price_percentage) / 100, x[2]),
                    listing_id_list))
        elif self.sell_price_type == 2:
            listing_id_list_map = list(
                map(lambda x: (x[0], x[1], x[1] * (100 + self.price_percentage) / 100, x[2]),
                    listing_id_list))
        elif self.sell_price_type == 3:
            listing_id_list_map = list(
                map(lambda x: (x[0], x[1], x[1] - self.price_percentage, x[2]), listing_id_list))
        elif self.sell_price_type == 4:
            listing_id_list_map = list(
                map(lambda x: (x[0], x[1], x[1] + self.price_percentage, x[2]), listing_id_list))
        # offer_id,price,adjust_price,sku  ->  adjust_price 变成2位小数
        listing_id_list_map = list(
            map(lambda x: (x[0], x[1], x[2].quantize(decimal.Decimal('0.01')), x[3]), listing_id_list_map))

        # 价格区间
        if self.price_adjust_type == 1:
            # 过滤数据
            filter_listing_id_list = list(
                filter(lambda x: (x[1] * self.min_price / 100) <= x[2] <= (x[1] * self.max_price / 100),
                       listing_id_list_map))
        # 利润区间
        elif self.price_adjust_type == 2:
            sku_GrossProfitMargin_map = self.getCalculationGrossProfitMarginViewModelList(
                self.calculator_formula_id, self.source_id, listing_id_list_map)
            listing_id_list_map = list(
                map(lambda x: (
                x[0], x[1], x[2], sku_GrossProfitMargin_map.get(x[3] + "@" + '{:.2f}'.format(x[2])), x[3]),
                    listing_id_list_map))

            # 过滤掉利润率负数
            def negative(listing_id_list_map):
                a = listing_id_list_map[3]
                return not float(a) < 0.00

            filter_listing_id_list = list(filter(negative, listing_id_list_map))
            filter_listing_id_list = list(
                filter(lambda x: self.min_profit <= Decimal(x[3]) <= self.max_profit,
                       filter_listing_id_list))
        return filter_listing_id_list

    def handlerCalculatorFormulas(self, listing_id_list):
        """
        调价方案： 计算器公式
        根据计算器公式，得到调整后的价格  验证是否在  原始价格*self.min_price ——  原始价格*self.max_price之间
        :param listing_id_list:
        :return:
        """
        # map数据
        listing_id_list_map = list(
            map(lambda x: (x[0], x[1], self.get_price(self.calculator_formula_id, self.source_id, x[2]), x[2]),
                listing_id_list))
        # 过滤数据
        filter_listing_id_list = list(
            filter(lambda x: (x[1] * self.min_price / 100) <= x[2] <= (x[1] * self.max_price / 100),
                   listing_id_list_map))
        return filter_listing_id_list

    def get_t_allegro_listing_list_by_specify(self, t_allegro_listing_template_detail_modb):
        t_allegro_listing_list = None
        if self.sku_list:
            filter_sql = {
                "$and": [
                    {"sku": {"$in": self.sku_list}},
                ]
            }
            projection1 = {"_id": 1, "offerId": 1, "sku": 1, "sourceSku": 1}
            t_allegro_listing_list = t_allegro_listing_template_detail_modb.select_skip_projection_limit(filter_sql,
                                                                                                         projection1,
                                                                                                         "_id", 10000)
        elif self.source_sku_list:
            filter_sql = {
                "$and": [
                    {"sourceSku": {"$in": self.source_sku_list}},
                ]
            }
            projection1 = {"_id": 1, "offerId": 1, "sku": 1, "sourceSku": 1}
            t_allegro_listing_list = t_allegro_listing_template_detail_modb.select_skip_projection_limit(filter_sql,
                                                                                                         projection1,
                                                                                                         "_id", 10000)
        elif self.offer_id_list:
            filter_sql = {
                "$and": [
                    {"offerId": {"$in": self.offer_id_list}},
                ]
            }
            projection1 = {"_id": 1, "offerId": 1, "sku": 1, "sourceSku": 1}
            t_allegro_listing_list = t_allegro_listing_template_detail_modb.select_skip_projection_limit(filter_sql,
                                                                                                         projection1,
                                                                                                         "_id", 10000)
        # 如果策略选择了SKU分级策略，取交集
        if self.sku_template_id:
            t_allegro_listing_list = self.get_t_allegro_listing_list_by_t_sku_strategy_relation(t_allegro_listing_list)
        # 如果是调价模板
        if self.strategy_type == 5:
            offer_id_set = self.get_offer_id_set_by_adjust_price(t_allegro_listing_list)
        else:
            offer_id_set = set(map(lambda x: x.get('offerId'), t_allegro_listing_list))
        return offer_id_set

    def get_offer_id_set_by_adjust_price(self, t_allegro_listing_list):
        current_offer_id_set = set(map(lambda x: x['offerId'], t_allegro_listing_list))
        current_offer_id_filter_adjuct_price_model_set = self.filter_adjust_model(current_offer_id_set)
        INFO.logger.info(f"调价模板过滤掉了{current_offer_id_set - current_offer_id_filter_adjuct_price_model_set}")
        return current_offer_id_filter_adjuct_price_model_set

    def get_t_allegro_listing_list_by_t_sku_strategy_relation(self, t_allegro_listing_list):
        sku_list = list(map(lambda x: x['sku'], t_allegro_listing_list))
        # 取交集
        filters_ObjectId_exist = {
            "$and": [
                {"strategy_relation.strategy_id": self.sku_template_id},
                {"sku": {"$in": sku_list}}
            ]
        }
        filters_ObjectId_exist_projection = {"sku": 1}
        actual_sku_list_filter = self.t_sku_strategy_relation_modb.select(filters_ObjectId_exist,
                                                                          filters_ObjectId_exist_projection)
        actual_sku_set_map = list(map(lambda x: x['sku'], actual_sku_list_filter))
        return list(filter(lambda x: x['sku'] in actual_sku_set_map, t_allegro_listing_list))


if __name__ == '__main__':
    ExecuteStrategy(346, 9046).execute_strategy()
    ExecuteStrategy(346, 22936).execute_strategy()
    ExecuteStrategy(346, 23358).execute_strategy()
    # ExecuteStrategy(1,9058).check_actual_offer_id_set()
